package com.ebelter.scaleblesdk.ble.upgrade;

import android.content.Context;

import com.ebelter.scaleblesdk.ble.bluetooth.callback.FirmwareUpgradeCallback;
import com.ebelter.scaleblesdk.ble.bluetooth.device.WeightDataHandle;
import com.ebelter.scaleblesdk.ble.bluetooth.impl.IFirmwareUpgradeCallback;
import com.ebelter.scaleblesdk.model.FileType;
import com.ebelter.scaleblesdk.model.Scale;
import com.ebelter.scaleblesdk.util.AssetsLoader;
import com.ebelter.scaleblesdk.util.BtThreadPool;
import com.ebelter.scaleblesdk.util.ULog;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 固件升级工具
 * Created by Sergio Pan on 2017/11/9.
 */

public class FirmwareUpgrader {

    private static FirmwareUpgrader instance = new FirmwareUpgrader();
    private final static String TAG = "FirmwareUpgrader";

    /**
     * OTA 升级类型-蓝牙
     */
    public final static int OTA_TYPE_BLE = 1;

    /**
     * OTA 升级类型-秤
     */
    public final static int OTA_TYPE_SCALE = 2;

    /**
     * OTA 升级类型-系数
     */
    public final static int OTA_TYPE_COEFFIIENT = 4;

    /**
     * OTA 升级类型-公式
     */
    public final static int OTA_TYPE_FORMULA = 8;

    //正在升级的固件文件类型
    private static FileType upgradingType = null;
    private Map<String, FileType> mRequests = new HashMap<>();
    private FirmwareUpgradeCallback customFirmwareUpgradeCallback;
    private boolean isStop = false;
    private WeightDataHandle mWeightDataHandle;
    private Context mContext;

    private String scaleFileName;
    private String bleFileName;


    private FirmwareUpgrader() {
    }

    public static FirmwareUpgrader getInstance() {
        return instance;
    }

    /**
     * 初始化固件升级工具
     *
     * @param ctx
     * @param handle
     */
    public void init(Context ctx, WeightDataHandle handle) {
        this.mContext = ctx;
        this.mWeightDataHandle = handle;
        //读取固件文件名
        instance.scaleFileName = AssetsLoader.getFirmwareMatchFileName(ctx, "OTA");
        instance.bleFileName = AssetsLoader.getFirmwareMatchFileName(ctx, "BLE");

        AssetsLoader.loadAssetsFirmwareFile(ctx, FileType.SCALE_FIRMWARE, instance.scaleFileName);
        AssetsLoader.loadAssetsFirmwareFile(ctx, FileType.BLE_FIRMWARE, instance.bleFileName);

        int scaleLength = AssetsLoader.getFirmwarePackageLength(FileType.SCALE_FIRMWARE);
        int bleLength = AssetsLoader.getFirmwarePackageLength(FileType.BLE_FIRMWARE);

        ULog.i(TAG, "---------init------------scaleFileName = " + scaleFileName + ", bleFileName = " + bleFileName);
        ULog.i(TAG, "---------init------------scaleLength = " + scaleLength + ", bleLength = " + bleLength);
    }

    public void registerFirmwareUpgradeCallback(FirmwareUpgradeCallback callback) {
        this.customFirmwareUpgradeCallback = callback;
    }

    /**
     * 转成固件版本
     *
     * @param hex
     * @return
     */
    public static int toFirmwareVersion(String hex) {
        int version = 0;
        try {
            version = Integer.parseInt(hex, 16);
            ;
        } catch (Exception e) {
            ULog.e(TAG, "-----toFirmwareVersion------e = " + e.getMessage());
        }
        return version;
    }

    /**
     * 转成用于显示的固件版本
     *
     * @param scaleVer
     * @return
     */
    public static String toFirmwareVersionShow(int scaleVer) {
        int h8Bit = Integer.parseInt(Integer.toHexString((scaleVer >> 8) & 127));
        return new StringBuffer().append(h8Bit / 10).append(".").append(h8Bit % 10).append(".").append(scaleVer & 255).toString();
    }

    /**
     * 获取秤固件文件版本
     *
     * @return
     */
    public static int getScaleFirmwareVersion() {
        return instance.getScaleVersionHex(instance.scaleFileName);
    }

    /**
     * 获取蓝牙固件文件版本
     *
     * @return
     */
    public static int getBleFirmwareVersion() {
        return instance.getBleVersion(instance.bleFileName);
    }

    public int getScaleVersionHex(String scaleFile) {
        int version = 0;
        if (scaleFile == null || scaleFile.isEmpty()) {
            return version;
        }
        String[] arr = scaleFile.split("_");
        if (arr != null && arr.length > 1) {
            String verStr = arr[1];
            version = toFirmwareVersion(verStr.substring(1, verStr.length()));
        }
        return version;
    }

    public int getBleVersion(String bleFile) {
        int version = 0;
        if (bleFile == null || bleFile.isEmpty()) {
            return version;
        }
        String[] arr = bleFile.split("_");
        if (arr != null && arr.length > 1) {
            String versionStr = arr[1];
            version = Integer.parseInt(versionStr.substring(1, versionStr.length()));
        }
        return version;
    }

    public boolean isUpgradable(int versionInScale, int versionInNewFile) {
        boolean update = false;
        if (versionInNewFile > versionInScale) {
            update = true;
        }
        return update;
    }

    /**
     * 秤固件是否可升级
     *
     * @param versionInScale
     * @return
     */
    public static boolean isScaleUpgradable(int versionInScale) {
        return instance.isUpgradable(versionInScale, getScaleFirmwareVersion());
    }

    /**
     * 蓝牙固件是否可升级
     *
     * @param versionInScale
     * @return
     */
    public static boolean isBleUpgradable(int versionInScale) {
        return instance.isUpgradable(versionInScale, getBleFirmwareVersion());
    }


    /**
     * 请求固件升级
     *
     * @param fileType 固件类型
     */
    public synchronized static void requestFirmwareUpgrade(FileType fileType) {
        ULog.i(TAG, "-----requestFirmwareUpgrade------fileType = " + fileType);
        int type, pkgLen, cs;
        type = pkgLen = cs = -1;

        switch (fileType) {
            case BLE_FIRMWARE:
                type = 1;
                pkgLen = AssetsLoader.getFirmwarePackageLength(FileType.BLE_FIRMWARE);
                cs = AssetsLoader.getFirmwareCrc8Check(FileType.BLE_FIRMWARE);
                break;
            case SCALE_FIRMWARE:
                type = 2;
                pkgLen = AssetsLoader.getFirmwarePackageLength(FileType.SCALE_FIRMWARE);
                cs = AssetsLoader.getFirmwareCrc8Check(FileType.SCALE_FIRMWARE);
                break;
        }
        ULog.i(TAG, "-----requestFirmwareUpgrade------type = " + type + ", pkgLen = " + pkgLen + ", cs = " + cs);
        //TODO CRC加密处理
        if (upgradingType == null) {
            upgradingType = fileType;
            instance.mRequests.put(fileType.toString(), fileType);
            instance.sendOtaUpgradeRequestCRC(type, pkgLen, cs);
        } else if (upgradingType == fileType) {
            //忽略与正在升级固件类型相同的请求
            ULog.e(TAG, "-----requestFirmwareUpgrade-----err: We have ignored this request, because this request is excuting !");
        } else {
            FileType mFileType = instance.mRequests.get(fileType.toString());
            //忽略正在排队固件类型相同的请求，防止线程冗余
            if (mFileType != fileType) {
                instance.mRequests.put(fileType.toString(), fileType);
                instance.queueToUpgrade(fileType, type, pkgLen, cs);
            } else {
                ULog.e(TAG, "-----requestFirmwareUpgrade-----err: We have ignored this request, because this request is queuing up !");
            }
        }
    }

    private void sendOtaUpgradeRequestCRC(final int type, final int pkgLen, final int cs) {
        ULog.i(TAG, "-----sendOtaUpgradeRequestCRC------type = " + type + ", pkgLen = " + pkgLen + ", cs = " + cs);

        if (mWeightDataHandle != null) {
            mWeightDataHandle.sendOTAUpdateRequestCRC(type, pkgLen, cs);
        }

    }

    /**
     * 排队升级
     *
     * @param type
     * @param pkgLen
     * @param cs
     */
    private void queueToUpgrade(final FileType fileType, final int type, final int pkgLen, final int cs) {
        BtThreadPool.addFixedThreadPoll(new Runnable() {
            @Override
            public void run() {
                while (upgradingType != null && !isStop) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    ULog.i(TAG, "-----queueToUpgrade------upgradingType = " + upgradingType);
                }
                isStop = true;
                upgradingType = fileType;
                sendOtaUpgradeRequestCRC(type, pkgLen, cs);
            }
        });
    }


    private byte getXor(byte[] bytes, byte xorByte) {
        int bsLen = bytes.length;
        for (int i = 0, j = 0; i < 16; i++, j++) {
            if (j >= bsLen) {
                xorByte ^= 0xff;
            } else {
                xorByte ^= bytes[j];
            }
        }
        return xorByte;
    }

    /**
     * CRC8校验
     *
     * @param bytes
     * @param result
     * @return
     */
    private byte getCrc8Check(byte[] bytes, byte result) {
        int size = bytes.length;

        for (int i = 0; i < size; i++) {
            result ^= bytes[i];
            for (int j = 0; j < 8; j++) {
                if ((result & 0x80) > 0) {
                    result <<= 1;
                    result = (byte) (result ^ 7);
                } else {
                    result <<= 1;
                }
            }
        }
        return result;
    }


    /**
     * 固件升级监听回调
     */
    private IFirmwareUpgradeCallback mIFirmwareUpgradeCallback = new IFirmwareUpgradeCallback() {
        @Override
        public void onGotScaleVersion(int bleVer, int scaleVer, int coefficientVer, int arithmeticVer) {
            ULog.i(TAG, "-----onGotScaleVersion------scaleVer = " + scaleVer);
            Scale.getInstance().setOtaVersion(scaleVer);
        }

        @Override
        public void onUpgradeResponse(int pkgNo, boolean result) {
            ULog.i(TAG, "-----onUpgradeResponse------pkgNo = " + pkgNo + ", result = " + result);

            int currProgress = pkgNo;
            List<byte[]> packages = null;
            if (upgradingType == FileType.SCALE_FIRMWARE) {
                packages = AssetsLoader.getFirmwarePackagesByte(FileType.SCALE_FIRMWARE);
            } else if (upgradingType == FileType.BLE_FIRMWARE) {
                packages = AssetsLoader.getFirmwarePackagesByte(FileType.BLE_FIRMWARE);
            }

            if (mWeightDataHandle != null && packages != null) {
                if (result) {//成功则发下一包，失败发原包
                    currProgress += 1;
                    pkgNo += 1;
                }
                if (currProgress < packages.size()) {
                    mWeightDataHandle.sendOTAPackage(packages.get(currProgress), pkgNo);
                }
            }


            if (customFirmwareUpgradeCallback != null && packages != null) {
                int percent = (int) (100 * (pkgNo * 1.0f / packages.size()));
                customFirmwareUpgradeCallback.onUpgradeResponse(percent);
            }
        }

        @Override
        public void onUpgradeResult(int result, int type) {
            ULog.i(TAG, "-----onUpgradeResult------result = " + result + ", type = " + type);
            //固件升级操作完成，并发处理，移除map里的文件类型，upgradingType置为null
            // TODO  还要处理，下发告诉秤的逻辑
            //下发升级结束指令
            mWeightDataHandle.sendOtaFinishCmd();

            mRequests.remove(upgradingType.toString());
            upgradingType = null;

            if (result == 0) {//固件升级成功

            }
            if (customFirmwareUpgradeCallback != null) {
                customFirmwareUpgradeCallback.onUpgradeResult(result, type);
            }

        }

        @Override
        public void onLowPower() {
            ULog.i(TAG, "-----onLowPower------");
            if (customFirmwareUpgradeCallback != null) {
                customFirmwareUpgradeCallback.onLowPower();
            }
        }

        @Override
        public void onOtaUpgradeReady(boolean result) {
            ULog.i(TAG, "-----onOtaUpgradeReady------result = " + result);
            List<byte[]> packages = null;
            if (upgradingType == FileType.SCALE_FIRMWARE) {
                packages = AssetsLoader.getFirmwarePackagesByte(FileType.SCALE_FIRMWARE);
            } else if (upgradingType == FileType.BLE_FIRMWARE) {
                packages = AssetsLoader.getFirmwarePackagesByte(FileType.BLE_FIRMWARE);
            }
            if (mWeightDataHandle != null && packages != null) {
                mWeightDataHandle.sendOTAPackage(packages.get(0), 0);
            }
        }
    };

    public IFirmwareUpgradeCallback getFirmwareUpgradeCallback() {
        return mIFirmwareUpgradeCallback;
    }

    public static void release() {
        instance.isStop = true;
        if (instance.mRequests != null) {
            instance.mRequests.clear();
            instance.mRequests = null;
        }
        instance.customFirmwareUpgradeCallback = null;
        instance.mContext = null;
    }

}
